{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import os\n",
    "from dotenv import load_dotenv\n",
    "# Load environment variables from openai.env file\n",
    "load_dotenv(\"openai.env\")\n",
    "\n",
    "# Read the OPENAI_API_KEY from the environment\n",
    "api_key = os.getenv(\"OPENAI_API_KEY\")\n",
    "api_base = os.getenv(\"OPENAI_API_BASE\")\n",
    "os.environ[\"OPENAI_API_KEY\"] = api_key\n",
    "os.environ[\"OPENAI_API_BASE\"] = api_base"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Prompt+LLM\n",
    "基本构成： \n",
    "PromptTemplate / ChatPromptTemplate -> LLM / ChatModel -> OutputParser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(\"给我讲一个关于{foo}的笑话\")\n",
    "model = ChatOpenAI(\n",
    "    temperature=0,\n",
    "    model=\"gpt-3.5-turbo\",\n",
    ")\n",
    "chain = prompt | model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='好的，这是一个关于狗熊的笑话：\\n\\n有一天，一只狗熊走进了一家酒吧。他走到吧台前，对酒保说：“请给我一杯啤酒。”\\n\\n酒保看着狗熊，惊讶地问道：“你会喝酒吗？”\\n\\n狗熊回答道：“当然会！我是狗熊啊！”\\n\\n酒保犹豫了一下，然后说：“好吧，那你给我证明一下。”\\n\\n狗熊点了点头，然后开始喝啤酒。他一口气喝下了整整一杯，然后把杯子放在吧台上。\\n\\n酒保惊讶地说：“哇，你真的会喝酒！”\\n\\n狗熊笑了笑，说：“当然了，我是狗熊啊！”\\n\\n酒保好奇地问：“那你能不能再喝一杯？”\\n\\n狗熊点了点头，然后又一口气喝下了第二杯啤酒。\\n\\n酒保惊讶地说：“太厉害了！你真的是个会喝酒的狗熊！”\\n\\n狗熊笑了笑，说：“当然了，我是狗熊啊！”\\n\\n酒保继续问：“那你能不能再喝一杯？”\\n\\n狗熊点了点头，然后又一口气喝下了第三杯啤酒。\\n\\n酒保惊讶地说：“你真的是个了不起的狗熊！”\\n\\n狗熊笑了笑，说：“当然了，我是狗熊啊！”\\n\\n酒保好奇地问：“那你能不能再喝一杯？”\\n\\n狗熊摇了摇头，说：“不行了，我得回家了。”\\n\\n酒保奇怪地问：“为什么？你不是说你是个会喝酒的狗熊吗？”\\n\\n狗熊笑了笑，说：“是啊，我是个会喝酒的狗熊，但我妈妈说，喝酒后不准开车。”')"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"foo\": \"狗熊\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "自定义停止输出符"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "chain = prompt | model.bind(stop=[\"\\n\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='好的，这是一个关于狗熊的笑话：')"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"foo\": \"狗熊\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "兼容openai函数调用的方式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "functions = [\n",
    "    {\n",
    "        \"name\": \"joke\",\n",
    "        \"description\": \"讲笑话\",\n",
    "        \"parameters\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "                \"setup\": {\"type\": \"string\", \"description\": \"笑话的开头\"},\n",
    "                \"punchline\": {\n",
    "                    \"type\": \"string\",\n",
    "                    \"description\": \"爆梗的结尾\",\n",
    "                },\n",
    "            },\n",
    "            \"required\": [\"setup\", \"punchline\"],\n",
    "        },\n",
    "    }\n",
    "]\n",
    "chain = prompt | model.bind(function_call={\"name\": \"joke\"}, functions=functions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\\n  \"setup\": \"为什么男人总是喜欢开玩笑？\",\\n  \"punchline\": \"因为他们觉得自己是个笑话！\"\\n}', 'name': 'joke'}})"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"foo\": \"男人\"}, config={})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "输出解析器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "chain = prompt | model | StrOutputParser()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'好的，这是一个关于女人的笑话：\\n\\n有一天，一个女人走进一家宠物店，她对店主说：“我想买一只特别聪明的宠物。”店主笑着回答：“我有一只非常聪明的鹦鹉，它会说七种语言。”女人很兴奋地问：“真的吗？那你能让它给我说说吗？”店主点了点头，然后对鹦鹉说：“鹦鹉，给这位女士说说你会的语言。”鹦鹉立刻回答：“当然！你好，你好，你好，你好，你好，你好，你好！”女人有些失望地说：“这只鹦鹉只会说一句话。”店主笑着说：“是的，但是它会用七种不同的语言说。”'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"foo\": \"女人\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "与函数调用混合使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser\n",
    "\n",
    "chain = (\n",
    "    prompt\n",
    "    | model.bind(function_call={\"name\": \"joke\"}, functions=functions)\n",
    "    | JsonOutputFunctionsParser()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'setup': '为什么女人喜欢逛街？', 'punchline': '因为逛街是唯一能让她们花钱时不觉得心疼的事情。'}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"foo\": \"女人\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "#只输出setup\n",
    "from langchain.output_parsers.openai_functions import JsonKeyOutputFunctionsParser\n",
    "from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n",
    "\n",
    "chain = (\n",
    "    {\"foo\": RunnablePassthrough()} #使用RunnablePassthrough()跳过prompt\n",
    "    | prompt\n",
    "    | model.bind(function_call={\"name\": \"joke\"}, functions=functions)\n",
    "    | JsonKeyOutputFunctionsParser(key_name=\"punchline\") # 定义输出的key\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'因为打折时可以一举两得，既能减肥又能增加衣柜！'"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#chain.invoke({\"foo\": \"女人\"})\n",
    "#使用RunnablePassthrough()跳过prompt\n",
    "chain.invoke(\"女人\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchains",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
         